package ldh.im.fxgui.controller;

import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.*;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import ldh.im.ImConfig;
import ldh.im.dto.base.Dto;
import ldh.im.dto.business.av.ChatDataReq;
import ldh.im.dto.business.message.MessageUserSendInfo;
import ldh.im.dto.enums.ByteType;
import ldh.im.fxbase.data.pojo.ImGroupUser;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.data.pojo.ImUser;
import ldh.im.fxbase.service.AudioSocketService;
import ldh.im.fxbase.service.AvService;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.handle.AvHandler;
import ldh.im.fxgui.util.UserHeadImageUtil;
import ldh.im.util.ByteCompressUtil;
import ldh.im.util.LocalSeqUtil;
import ldh.im.util.Sets;
import ldh.rpc.Receive;
import ldh.rpc.cache.CacheService;
import ldh.rpc.client.RpcClientBootstrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;

// 视频聊天
public class VideoController implements Receive<Dto>, Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(VideoController.class);

    @FXML private Node root;
    @FXML private Pane flowPane;

    private ImSession imSession;
    private volatile String groupCode;
    private volatile AudioSocketService playAudioSocketService = null;
    private volatile AvService avService = null;
    private Executor executor = Executors.newCachedThreadPool();
    private Map<String, ImageView> userImageViewMap = new HashMap<>();

    private AvHandler avHandler = null;

    public void setParams(RpcClientBootstrap rpcClientBootstrap, ImSession imSession, String groupId, Set<String> userIdSet) {
        this.avHandler = new AvHandler(rpcClientBootstrap);

        this.imSession = imSession;

        if (imSession.isP2p()) {
            initP2pPane(userIdSet);
        } else {
            initGroupPane(userIdSet);
        }

        ThreadPoolUtil.submit(()->{
            ExceptionUtil.catchException(()->{
                CacheService cacheService = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService();
                String userId = (String) cacheService.getCache(ImConfig.LOGIN_USER_ID);
                avHandler.login();

                if (groupId == null) {
                    groupCode = avHandler.createGroup(LocalSeqUtil.createLocalSeq(), Sets.asSet(userId, imSession.getToUserId()));

                    startVideo();
                } else {
                    avHandler.addGroup(groupId, Sets.asSet(userId));
                }
                return null;
            }, "视频聊天");
        });
    }

    @FXML
    public void close() throws Exception {
        if (avService != null) {
            avService.close();
        }

        Window window = root.getScene().getWindow();
        if (window instanceof Stage) {
            ((Stage) window).close();
        }
    }

    @Override
    public void receive(Dto data) {
        if (!(data instanceof ChatDataReq)) return;
        if (playAudioSocketService == null) {
            playAudioSocketService = new AudioSocketService();
            playAudioSocketService.startStream();
        }
        executor.execute(()->{
            ChatDataReq chatDataReq = (ChatDataReq) data;
            try {
                if (chatDataReq.getByteType() == ByteType.audio) {
                    byte[] unzipBytes = ByteCompressUtil.unZip(chatDataReq.getData());
                    if (unzipBytes == null) return;
                    playAudioSocketService.playAudio(unzipBytes, unzipBytes.length);
                } else if(chatDataReq.getByteType() == ByteType.av) {
                    byte[] videoData = ByteCompressUtil.unZip(chatDataReq.getData());
                    ImageView imageView = userImageViewMap.get(chatDataReq.getFromUserId());
                    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(videoData);
                    BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);

                    Image image = SwingFXUtils.toFXImage(bufferedImage, null);
                    FxThreadUtil.runLater(() -> imageView.setImage(image));
                } else {
                    throw new RuntimeException("不支持这种类型:" + chatDataReq.getByteType());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void startVideo() {
        try {
            Consumer<AvService.AvData> consumer = (chatData)->{
                try {
                    if (chatData.getByteType() == ByteType.audio) {
                        byte[] zipBytes = ByteCompressUtil.zip(chatData.getData(), 0, chatData.getLength());
                        avHandler.sendAudioData(groupCode, zipBytes, zipBytes.length);
                    } else if (chatData.getByteType() == ByteType.av) {
                        if (imSession.isP2p()) {
                            ImageView imageView = userImageViewMap.get(imSession.getToUserId());
                            FxThreadUtil.runLater(()->imageView.setImage(chatData.getImage()));
                        } else {
                            CacheService cacheService = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService();
                            String userId = (String) cacheService.getCache(ImConfig.LOGIN_USER_ID);
                            ImageView imageView = userImageViewMap.get(userId);
                            FxThreadUtil.runLater(()->imageView.setImage(chatData.getImage()));
                        }

                        BufferedImage bufferedImage = SwingFXUtils.fromFXImage(chatData.getImage(), null);
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        ImageIO.write(bufferedImage, "png",  bos);
                        byte[] buffer = bos.toByteArray();
                        byte[] avBytes = ByteCompressUtil.zip(buffer);

                        avHandler.sendVideoData(groupCode, avBytes, avBytes.length);
                    } else {
                        throw new RuntimeException("不支持这种类型:" + chatData.getByteType());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
            avService = new AvService(consumer);
            avService.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {

    }

    private void initGroupPane(Set<String> userIdSet) {
        userIdSet.forEach(userId->{
            Pane pane = createVideo(userId);
            flowPane.getChildren().addAll(pane);
        });
    }

    private void initP2pPane(Set<String> userIdSet) {
        Pane pane = createVideo(imSession.getToUserId());
        flowPane.getChildren().addAll(pane);
    }

    private Pane createVideo(String userId) {
        VBox vBox = new VBox();
        vBox.setAlignment(Pos.CENTER);
        ImageView imageView = new ImageView();
        imageView.setFitWidth(80);
        imageView.setFitHeight(80);
        ImUser user = ImCacheFactory.getInstance().getUser(userId);
        Image image = UserHeadImageUtil.getHeadImage(user.getHeadImg());
        imageView.setImage(image);
        userImageViewMap.put(imSession.getToUserId(), imageView);

        Label label = new Label(user.getUserName());
        vBox.getChildren().addAll(imageView, label);
        vBox.setSpacing(10);
        return vBox;
    }

    private Set<MessageUserSendInfo> buildUserIdSet(String userId, ImSession imSession) {
        if (imSession.isP2p()) {
            return Sets.asSet(buildMessageToUserInfo(userId, imSession));
        } else {
            return imSession.getImGroup().getGroupUserSet().stream().map(u->buildMessageToUserInfo(userId, u)).collect(Collectors.toSet());
        }
    }

    // todo
    private MessageUserSendInfo buildMessageToUserInfo(String userId, ImSession imSession) {
        MessageUserSendInfo messageUserSendInfo = new MessageUserSendInfo();
        messageUserSendInfo.setUserId(imSession.getFromUserId());
        if (userId.equals(imSession.getFromUserId())) {
            messageUserSendInfo.setUserId(imSession.getToUserId());
        }
        return messageUserSendInfo;
    }

    private MessageUserSendInfo buildMessageToUserInfo(String userId, ImGroupUser groupUser) {
        MessageUserSendInfo messageUserSendInfo = new MessageUserSendInfo();
        messageUserSendInfo.setUserId(groupUser.getUserId());
        return messageUserSendInfo;
    }
}
